﻿/*=============================================================================
* File Name : Plane.cpp                         Ver : 1.00  Date : 2009-3-26
*
* Description :
*
*       平面クラス　ソース
*
* Author : 土田　悠貴.
*============================================================================*/
#include "Plane.h"
#include "../math/Vector3.h"
#include "../math/Matrix3.h"
#include "../math/Matrix4.h"
#include "../math/Math.h"

#include <cstdio>
#include <cfloat>

namespace tglib
{

/*===========================================================================*/
/**
 * コンストラクタ．
 */
Plane::Plane() : a( 0.0f ), b( 0.0f ), c( 0.0f ), d( 0.0f )
{}

/*===========================================================================*/
/**
 * コンストラクタ．
 *
 * @param [in] [in]    A　面法線ｘ成分．
 * @param [in] [in]    B　面法線ｙ成分．
 * @param [in] [in]    C　面法線ｚ成分．
 * @param [in] [in]    D　原点からの距離．
 */
Plane::Plane( float A, float B, float C, float D ) :
	a( A ), b( B ), c( C ), d( D )
{}

/*===========================================================================*/
/**
 * コンストラクタ．
 *
 * @param [in] [in]    N　法線ベクトル．
 * @param [in] [in]    D　原点からの距離．
 */
Plane::Plane( const Vector3& N, float D ) :
	a( N.x ), b( N.y ), c( N.z ), d( D )
{}

/*===========================================================================*/
/**
 * コンストラクタ．
 *
 * @param [in] [in]    P　平面上の座標．
 * @param [in] [in]    N　法線ベクトル．
 */
Plane::Plane( const Vector3& P, const Vector3& N )
{
	a = N.x;
	b = N.y;
	c = N.z;
	d = -N.dot( P );
}

/*===========================================================================*/
/**
 * コンストラクタ．
 *
 * @param [in] [in]    V1 座標1.
 * @param [in] [in]    V2 座標2.
 * @param [in] [in]    V3 座標3.
 */
Plane::Plane( const Vector3& V1, const Vector3& V2, const Vector3& V3 )
{
	Vector3	Normal;
	Vector3	point( V1 );

	// 法線ベクトルを求める
	Normal= point.calculateNormal( V2, V3 );

	// 法線ベクトルと座標から平面のパラメータを求める
	a = Normal.x;
	b = Normal.y;
	c = Normal.z;
	d = -Normal.dot( V1 );
}

/*===========================================================================*/
/**
 * 正規化．
 *
 * @return 正規化した平面
 */
Plane& Plane::normalize()
{
	float	Len;

	Len = Math::SQR( a ) + Math::SQR( b ) + Math::SQR( c );

	if( Len != 0 )
	{
		a = a / Len;
		b = b / Len;
		c = c / Len;
		d = d / Len;
	}

	return *this;
}

/*===========================================================================*/
/**
 * 正規化した平面を得る．
 *
 * @return 正規化した平面
 */
Plane Plane::getNormalize()
{
	Plane	result( *this );

	result.normalize();

	return result;
}

/*===========================================================================*/
/**
 * 平面上の位置を取得．
 *
 * @return 平面上の座標
 */
Vector3 Plane::getPoint() const
{
	Vector3	point;

	point.x = a * ( -d );
	point.y = b * ( -d );
	point.z = c * ( -d );

	return point;
}

/*===========================================================================*/
/**
 * 平面とベクトルの内積を求める．
 *
 * @param [in] [in]  v 3Dベクトル.
 * @return 内積.
 */
float Plane::dot( const Vector3& v ) const
{
	return a * v.x + b * v.y + c * v.z;
}

/*===========================================================================*/
/**
 * 平面と点の距離を求める．
 *
 * @param [in] [in]  v 点座標.
 * @return 距離.
 */
float Plane::distance( const Vector3& v ) const
{
	return a * v.x + b * v.y + c * v.z + d;
}


/*===========================================================================*/
/**
 * 平面と線分が交差するか調べる．
 *
 * @param [in] [in]  Line0 始点座標.
 * @param [in] [in]  Line1 終点座標.
 * @return 交差しているならtrue, そうでないならfalse.
 */
bool Plane::isIntersectLine( const Vector3& line0, const Vector3& line1 ) const
{
	float Distance1;
	float Distance2;

	// 平面と始点の距離を求める
	Distance1 = distance( line0 );

	// 平面と終点の距離を求める
	Distance2 = distance( line1 );

	// 始点と終点の距離が同符号の場合は交差しない
	if( ( Distance1 * Distance2 ) > 0 )
	{
		return false;
	}

	return true;
}

/*===========================================================================*/
/**
 * 平面と線分の交点を取得する．
 *
 * @param [in] [in]  line0 始点座標.
 * @param [in] [in]  line1 終点座標.
 * @return 交差している座標.
 */
Vector3 Plane::getIntersectLine( const Vector3& line0, const Vector3& line1 ) const
{
	Vector3	result;

	// 平面と線分が交差しているか
	if( isIntersectLine( line0, line1 ) == false )
	{
		return result;
	}

	Vector3 dir = line1 - line0;

	return getIntersectRay( line0, dir );
}

/*===========================================================================*/
/**
 * 平面とレイが交差するか調べる．
 *
 * @param [in] [in]  rayPos レイの始点.
 * @param [in] [in]  rayDir レイの方向.
 * @return 交差しているならtrue, そうでないならfalse.
 */
bool Plane::isIntersectRay( const Vector3& rayPos, const Vector3& rayDir ) const
{
	// レイの方向を正規化する
	Vector3 normal( rayDir );
	normal.normalize();

	// 平面と直線が平行か?
	if( dot( normal ) == 0.0f )
	{
		return false;
	}

	return true;
}

/*===========================================================================*/
/**
 * 平面とレイの交点を取得する．
 *
 * @param [in] [in]  ray レイの始点.
 * @param [in] [in]  dir レイの方向.
 * @return 交差しているならtrue, そうでないならfalse.
 */
Vector3 Plane::getIntersectRay( const Vector3& ray, const Vector3& dir ) const
{
	// レイの方向を正規化する
	Vector3 normal( dir );
	normal.normalize();

	// 分母を求める
	float denominator = dot( normal );

	Vector3 result;

	// 平面と直線が平行か
	if( denominator == 0.0f )
	{
		return result;
	}

	// 分子を求める
	float numerator = -distance( ray );

	// 始点と平面の距離を求める
	float dis = numerator / denominator;

	// 交点を求める
	Vector3 intersect;
	intersect.linearEquation( ray, normal, dis );

	return intersect;

}

/*===========================================================================*/
/**
 * 平面と座標の位置関係を返す．
 *
 * @param [in] [in]  pos 調べる座標.
 * @return 位置関係.
 */
Plane::PlanePosition Plane::getClassifyPoint( const Vector3& pos ) const
{
	float Distance;

	// 平面と点との距離を求める
	Distance = distance( pos );


	// 平面の前面に点が存在するか？
	if( Distance > FLT_EPSILON )
	{
		return FRONT_PLANE;
	}

	// 平面の背面に点が存在するか？
	if( Distance < -FLT_EPSILON )
	{
		return BEHIND_PLANE;
	}

	// 平面上に点が存在する
	return ON_PLANE;
}

/*===========================================================================*/
/**
 * 平面と球体の位置関係を返す．
 *
 * @param [in] [in]  center 中心座標.
 * @param [in] [in]  radius 半径.
 * @return 位置関係.
 */
Plane::PlanePosition Plane::getClassifyShpere( const Vector3& center, float radius ) const
{
	float Distance;

	// 平面と中心座標の距離を求める
	Distance = dot( center ) + d;

	// 平面と球体が交差しているか？
	if( Math::ABS( Distance ) < radius )
	{
		return INTERSECTS_PLANE;
	}

	// 平面の前面に球体が存在するか？
	if( Distance >= radius )
	{
		return FRONT_PLANE;
	}

	// 平面上の背面に球体が存在する
	return BEHIND_PLANE;
}

/*===========================================================================*/
/**
 * 平面上の座標と平面の法線ベクトルから平面を作成．
 *
 * @param [in]   point 平面上の座標.
 * @param [in]   normal 平面の法線ベクトル.
 * @return 平面データ.
 */
Plane Plane::fromPointNrmal( const Vector3& point, const Vector3& normal )
{
	a = normal.x;
	b = normal.y;
	c = normal.z;
	d = -(normal.dot( point ) );

	return *this;
}

/*===========================================================================*/
/**
 * 平面上の座標と平面の法線ベクトルから平面を作成．
 *
 * @param [in]   point1 平面構成する座標1.
 * @param [in]   point2 平面構成する座標2.
 * @param [in]   point3 平面構成する座標3.
 * @return 平面データ.
 */
Plane Plane::fromPoints( const Vector3& point1, const Vector3& point2, const Vector3& point3 )
{
	Vector3 normal( point1 );

	// 法線ベクトルを求める
	normal = normal.calculateNormal( point2, point3 );

	// 法線ベクトルと座標から平面のパラメータを求める
	fromPointNrmal( point1, normal );

	return *this;
}

/*===========================================================================*/
/**
 * (+)単項演算子オーバーロード．
 *
 * @return 結果のPlane．
 */
Plane Plane::operator + () const
{
	return *this;
}

/*===========================================================================*/
/**
 * (-)単項演算子オーバーロード．
 *
 * @return 結果のPlane．
 */
Plane Plane::operator - () const
{
	return Plane( -a, -b, -c, -d );
}

/*===========================================================================*/
/**
 * (==) 演算子オーバーロード．
 *
 * @param [in]  Plane1 Plane．
 * @param [in]  Plane2 Plane．
 * @return 比較結果．
 */
bool operator == ( const Plane& plane1, const Plane& plane2 )
{
	return ( plane1.a == plane2.a &&
			 plane1.b == plane2.b &&
			 plane1.c == plane2.c &&
			 plane1.d == plane2.d );
}

/*===========================================================================*/
/**
 * (!=) 演算子オーバーロード．
 *
 * @param [in]  Plane1 Plane．
 * @param [in]  Plane2 Plane．
 * @return 比較結果．
 */
bool operator != ( const Plane& plane1, const Plane& plane2 )
{
	return !( plane1 == plane2 );
}

} // namespace tglib

/*========= End of File =====================================================*/
